import { Inject, Injectable } from '@nestjs/common';
import { RedisService } from '@/shared/redis.service';
import { Cron } from '@nestjs/schedule';
import { InjectRepository } from '@nestjs/typeorm';
import { Article, ArticleStatus } from '@/modules/article/entities/article.entity';
import { Between, LessThanOrEqual, Repository } from 'typeorm';

@Injectable()
export class RankingService {
  constructor(
    @Inject(RedisService)
    private readonly redisService: RedisService,
    @InjectRepository(Article)
    private readonly articleRepository: Repository<Article>,
  ) {}

  // 重置所有文章的浏览量
  async resetArticleViews() {
    let pageNo = 1;
    const pageSize = 100;

    let articles = await this.articleList({ pageNo, pageSize });

    while (articles.length > 0) {
      for (const article of articles) {
        await this.redisService.zAdd('article_views_ranking', { [article.article_id]: 0 });
      }

      pageNo++;
      articles = await this.articleList({ pageNo, pageSize });
    }
  }

  async articleList({ pageNo, pageSize }: { pageNo: number; pageSize: number }) {
    return this.articleRepository.find({
      where: {
        article_status: ArticleStatus.PUBLISHED,
        article_public_date: LessThanOrEqual(new Date()),
      },
      relations: { author: true, tags: true, categories: true },
      order: { update_date: 'desc' },
      skip: (pageNo - 1) * pageSize,
      take: pageSize,
    });
  }

  // 每周一的凌晨0点执行
  @Cron('0 0 * * 1')
  async handleCron() {
    await this.resetArticleViews();
    await this.updateWeeklyRanking();
  }

  // 更新周榜
  async updateWeeklyRanking() {
    const weeklyViews = await this.getWeeklyViews();
    for (const view of weeklyViews) {
      await this.redisService.zAdd('article_views_ranking', {
        [view.article_id]: view.article_views,
      });
    }
  }

  // 更新月榜
  async updateMonthlyRanking() {
    const monthlyViews = await this.getMonthlyViews();
    for (const view of monthlyViews) {
      await this.redisService.zAdd('article_views_ranking', {
        [view.article_id]: view.article_views,
      });
    }
  }

  // 每月一号的凌晨0点执行
  @Cron('0 0 1 * *')
  async handleMonthlyCron() {
    await this.updateMonthlyRanking();
  }

  // 获取一周内的浏览量数据
  async getWeeklyViews() {
    const now = new Date();
    const weeklyViews = [];
    for (let i = 0; i < 7; i++) {
      const date = new Date(now.getFullYear(), now.getMonth(), now.getDate() - i);
      const dailyViews = await this.getDailyViews(date);
      weeklyViews.push(...dailyViews);
    }
    return weeklyViews;
  }

  // 获取一天的浏览量数据
  async getDailyViews(date: Date) {
    const start = new Date(date.getFullYear(), date.getMonth(), date.getDate());
    const end = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1);
    return await this.articleRepository.find({
      select: ['article_id', 'article_title', 'article_views'],
      where: {
        update_date: Between(start, end),
      },
      order: {
        article_views: 'DESC',
      },
    });
  }

  // 获取实时排行榜
  async getRealTimeRanking() {
    return await this.articleRepository.find({
      select: ['article_id', 'article_title', 'article_views'],
      order: {
        article_views: 'DESC',
      },
      take: 8,
    });
  }

  // 通过缓存获取实时排行
  async getRealTimeRankingWithCache() {
    // 从 Redis 中获取排行榜
    const ranking = await this.redisService.get('real_time_ranking');

    // 如果 Redis 中没有数据，或者数据已经过期
    if (!ranking) {
      const rankingData = await this.getRealTimeRanking();

      // 这里设置缓存的过期时间为 12 小时
      await this.redisService.set('real_time_ranking', JSON.stringify(rankingData), 12 * 60 * 60);

      return rankingData;
    }

    return JSON.parse(ranking);
  }

  // 获取一个月内的浏览量数据
  async getMonthlyViews() {
    const now = new Date();
    const start = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate());
    const end = new Date(now.getFullYear(), now.getMonth(), now.getDate());
    return await this.articleRepository.find({
      select: ['article_id', 'article_title', 'article_views'],
      where: {
        update_date: Between(start, end),
      },
      order: {
        article_views: 'DESC',
      },
    });
  }
}
